package cmd

import (
	"context"
	"gf-admin/internal/consts"
	"gf-admin/internal/controller/system"
	"gf-admin/internal/dao"
	"gf-admin/internal/model"
	"gf-admin/internal/model/entity"
	"gf-admin/internal/service"
	"gf-admin/utility"
	"gf-admin/utility/response"
	"github.com/gogf/gf/v2/container/gvar"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"

	"github.com/goflyfox/gtoken/gtoken"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/gcmd"
)

var apiToken *gtoken.GfToken

var (
	Main = gcmd.Command{
		Name:  "main",
		Usage: "main",
		Brief: "start http server",
		Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
			s := g.Server()
			//todo 获取gtoken 的实例
			apiToken = &gtoken.GfToken{
				ServerName:       "beauty-service",
				CacheMode:        CfgGet(ctx, "gToken.CacheMode").Int8(),  //1表示用go-cache患者 2表示用redis缓存
				CacheKey:         CfgGet(ctx, "gToken.CacheKey").String(), //这个估计就是在redis中保存key
				Timeout:          CfgGet(ctx, "gToken.Timeout").Int(),
				MaxRefresh:       CfgGet(ctx, "gToken.MaxRefresh").Int(), //刷新时间
				EncryptKey:       CfgGet(ctx, "gToken.EncryptKey").Bytes(),
				AuthFailMsg:      CfgGet(ctx, "gToken.AuthFailMsg").String(),
				MultiLogin:       CfgGet(ctx, "gToken.MultiLogin").Bool(), //true为多端登录，false 单点登录
				LoginPath:        "/login",
				LogoutPath:       "/logout",      //退出登录
				LoginBeforeFunc:  loginFunc,      //登录时调用
				LoginAfterFunc:   loginAfterFunc, //登录后调用
				LogoutBeforeFunc: LogoutBeforeFunc,
				AuthPaths:        g.SliceStr{"/api/v1"},                                                              //拦截的路由
				AuthExcludePaths: g.SliceStr{"/api/v1/system/register", "/api/v1/swagger", "/api/v1/system/captcha"}, //这个是不拦截的路由
				AuthAfterFunc:    authAfterFunc,                                                                      //这个是拦截路由前校验
			}
			//todo 启动gtoken
			errs := apiToken.Start()
			if errs != nil {
				panic("gtoken 启动失败：" + errs.Error())
			}

			s.Group("/api/v1/system", func(group *ghttp.RouterGroup) {
				//group.Middleware(ghttp.MiddlewareHandlerResponse)
				group.Middleware(
					service.Middleware().CORS,
					service.Middleware().ResponseHandler,
				)
				// todo 绑定中间件 gToken
				err := apiToken.Middleware(ctx, group)
				if err != nil {
					panic(err.Error())
				}
				group.Bind(
					system.NewV1(),
				)
			})
			s.Run()
			return nil
		},
	}
)

// 登录时调用
func loginFunc(r *ghttp.Request) (string, interface{}) {
	userName := r.Get("user_name").String()
	passWord := r.Get("pass_word").String()
	if userName == "" || passWord == "" {
		response.Json(r, consts.ERROR, "账号或密码不能为空", g.Map{})
		r.ExitAll()
	}

	userInfo, err := service.SysUser().FindUserNameInfo(r.GetCtx(), userName)
	if err != nil {
		response.Json(r, consts.ERROR, "登录时查询数据库出错:"+err.Error(), g.Map{})
		r.ExitAll()
	}
	if userInfo == nil {
		response.Json(r, consts.ERROR, "登录失败,没有该账号", g.Map{})
		r.ExitAll()
	}
	if utility.EncryptPassword(passWord, userInfo.UserSalt) != userInfo.PassWord {
		response.Json(r, consts.ERROR, "账号密码错误", g.Map{})
		r.ExitAll()
	}
	if userInfo.UserStatus == 0 {
		response.Json(r, consts.ERROR, "您的账号已被冻结,请联系管理员!", g.Map{})
		r.ExitAll()
	}
	return consts.GTokenAdminPrefix + gconv.String(userInfo.Id), userInfo
}

// 登录成功后调用的函数
func loginAfterFunc(r *ghttp.Request, respData gtoken.Resp) {
	//g.Dump("respData:", respData)
	if respData.Code != 0 {
		r.Response.WriteJson(respData)
	}
	//获取登录用户id
	userKey := respData.GetString("userKey")
	//admin:1
	userId := gstr.StrEx(userKey, consts.GTokenAdminPrefix)
	if userId == "" {
		response.JsonExits(r, 400, "userId为空,该用户不存在!", nil)
		return
	}
	//获取用户信息
	info, err := service.SysUser().FindUserIdInfo(r.GetCtx(), gconv.Int(userId), respData.GetString("token"))
	if err != nil {
		return
	}

	userInfo := &model.UserInfo{
		Content: &model.Content{
			UserName:    info.UserName,
			NickName:    info.NickName,
			UserSalt:    info.UserSalt,
			UserStatus:  info.UserStatus,
			Avatar:      info.Avatar,
			DeptId:      info.DeptId,
			TokenType:   info.TokenType,
			AccessToken: info.AccessToken,
		},
	}

	response.Json(r, 0, "ok", userInfo)
}

func authAfterFunc(r *ghttp.Request, respData gtoken.Resp) {

	userDataStr := respData.GetString("data")
	if userDataStr == "" {
		response.JsonExits(r, -2, "非法请求,请先登录!", nil)
		return
	}

	var userInfo entity.SysUser
	err := gconv.Struct(userDataStr, &userInfo)
	if err != nil {
		response.JsonExits(r, -1, "数据结构转换出错!", nil)
		return
	}
	r.SetCtxVar(consts.USER_INFO, &userInfo)
	service.Middleware().AuthCasbin(r, userInfo.Id)
	r.Middleware.Next()
}

func LogoutBeforeFunc(r *ghttp.Request) bool {
	resp := apiToken.GetTokenData(r)
	var userInfo entity.SysUser
	err := gjson.DecodeTo(resp.GetString("data"), &userInfo)
	if err != nil {
		return false
	}
	conuts, err := dao.SysUser.Ctx(r.Context()).Where(dao.SysUser.Columns().Id, userInfo.Id).Count()
	if err != nil {
		return false
	}
	if conuts == 0 {
		return false
	}

	return true
}

func CfgGet(ctx context.Context, name string) *gvar.Var {
	gVar, _ := g.Config().Get(ctx, name)
	return gVar
}
